/******************************************************************************
* @(#) XMD.as A Flash/ActionScript XML utility class
* Copyright (c) 2003 Emmanuel Okyere <chief@okyere.org>
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://opensource.org/licenses/cpl.php
*
* Contributers:
* 		Emmanuel Okyere - initial API and implementation
*      Luar Yen<luar@luar.net, http://luar.com.hk/flashbook/> - Bug Fix and Enhanced Version
******************************************************************************/
import mx.events.EventDispatcher;
/**
* A utility class that extracts the data in xml files--attributes, nodes
* and associated values--and creates an object for use in ActionScript
* with Flash, based on the structure of the xml file. Multiple nodes
* with the same name at the same level--siblings with the same node name--
* are returned collectively as an Array, otherwise node names become keys
* for an associative array that maps to the individual node values.
*
*
* @version 1.0, 11/2003
*/
dynamic class XMD {
	private var data:Object, xml:Object;
	public var addEventListener:Function;
	public var removeEventListener:Function;
	public var dispatchEvent:Function;
	public var dispatchQueue:Function;
	private static var _mixin1 = new EventDispatcher.initialize(XMD.prototype);
	function XMD() {
		data = new Object();
		xml = new XML();
		xml.ignoreWhite = true;
		xml.delegator = this;
		xml.onLoad = function(success) {
			// Luar Hack for prevent infinite loop in non existing xml file
			if (success) {
				var data:Object = this.delegator.parse(this.firstChild);
				var cnt:Number = 0;
				for (var i in data) {
					++cnt;
					if (cnt>1) {
						break;
					}
				}
				if (cnt == 1) {
					data = data[i];
				}
				this.delegator.data = data;
				this.delegator.finalize();
			} else {
				var eventObj:Object = {target:this, type:"onXMDLoad"};
				eventObj.success = success;
				this.delegator.dispatchEvent(eventObj);
			}
		};
	}
	/**
	* Accepts a string representing the URL to the xml file to be parsed,
	* and loads the xml file. Once the file is successfully loaded, the
	* data in the xml file is extracted, and <code>onLoad</code> is called.
	*
	* @param	url	URL (String) to file containing xml structure to be parsed
	*
	* @see #onLoad()
	*/
	public function load(url:String) {
		xml.load(url);
	}
	/**
	* Called after the data from the xml file has been fully loaded and
	* extracted. At this point, calling <code>getData()</code> will return
	* all the data from the xml file converted into proper and associative
	* arrays (objects).
	* <p>
	* Developers *SHOULD* override this method
	* </p>
	*
	* @see #load()
	* @see #getData()
	*/
	public function onLoad() {
	}
	/**
	* Provides access to the parsed xml data. It is best to
	* call this method in the onLoad method which shd be overriden
	* by developers.
	*
	* @return	Returns the parsed data from the xml structure
	*
	* @see	#onLoad()
	*/
	public function getData() {
		return data;
	}
	/**
	* The meat of the class, it recursively converts an XMLNode object
	* and its childNodes to either an array or associative array, based
	* on the structure of the node.
	* <p>
	* At each level of the node and its childNodes, siblings with
	* identical names have their values coallesced into a single (proper) array
	* otherwise they are returned as an associative array.
	* </p>
	*
	* @param node The XMLNode to be parsed
	* @return Returns an Objectized equivalent of the node
	*
	* @see #getValue()
	* @see #getAttributes()
	*/
	private function parse(node:XMLNode):Object {
		var value:Object = new Object();
		var nodes:Number = node.childNodes.length;
		for (var i = 0; i != nodes; ++i) {
			var name:String = node.childNodes[i].nodeName;
			if (name != null) {
				if (value[name] != undefined) {
					if (!(value[name] instanceof Array)) {
						value[name] = new Array(value[name]);
					}
					value[name].push(getValue(node.childNodes[i]));
				} else {
					value[name] = getValue(node.childNodes[i]);
				}
			} else {
				value = getValue(node.childNodes[i]);
			}
		}
		var attributes:Object = getAttributes(node);
		if (attributes != null) {
			if (nodes != 0) {
				if (!(value instanceof XMLNode)) {
					for (var i in value) {
						attributes[i] = value[i];
					}
				} else {
					attributes['_val'] = value.nodeValue;
				}
			}
			return attributes;
		}
		return value;
	}
	/**
	* Queries the passed <code>XMLNode</code> for attributes and returns an
	* associative array, where attribute names act as keys that map to attribute
	* values. If no attributes are found, null is returned.
	*
	* @param node XML node from which to extract attributes
	* @return Returns an associative array of the attributes/vals found on
	*	   on the XMLNode passed to it, null otherwise
	*/
	private function getAttributes(node:XMLNode):Object {
		var attributes = new Object();
		for (var i in node.attributes) {
			attributes[i] = node.attributes[i];
		}
		return i != undefined ? attributes : null;
	}
	/**
	* Accepts an <code>XMLNode</code>, parses it if necessary, and
	* returns its value.
	*
	* @param node XML node from which to extract value
	* @return Returns the value of the node
	*/
	private function getValue(node:XMLNode):Object {
		switch (node.nodeType) {
		case 1 :
			return parse(node);
		case 3 :
		    // Luar Hack for return data type String, not Object
			return node.toString();
		}
		return null;
	}
	/**
	* cleans up any unnecessary data left lying around, and calls
	* <code>onLoad</code>
	*/
	private function finalize() {
		delete xml;
		/* Instead of calling an onLoad method, I have modified
		*  the code slightly to broadcast a message when the XML
		*  has been parsed
		*  - ddura
		*/
		var eventObj:Object = {target:this, type:"onXMDLoad"};
		eventObj.data = data;
		eventObj.success = true;
		dispatchEvent(eventObj);
		// onLoad();
	}
}
/*****************************************************************************
* changelog
* 5th Augest 2004, Return node value in String (original version, it's data type is Object)
* 5th Auguest 2004, Bug Fix by Luar for not exist XML cause infinite loop
* 02/02/2004 07:14 AM cleaned up doc
* 02/02/2004 08:00 AM added Event Broadcaster logic, removed onLoad call - ddura
*****************************************************************************/
